package mosync.mobilelualiveditor.ui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Map;

import javax.swing.AbstractButton;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JColorChooser;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JSplitPane;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

import org.apache.xerces.parsers.SAXParser;

class TestWidgetEditorListener implements WidgetEditorListener
{
	public void onWidgetCreated(WidgetInstance widget)
	{
		System.out.println("Widget " + widget + " created");
	}

	public void onWidgetNameChanged(WidgetInstance w, String nameBefore,
		String nameAfter)
	{
		System.out.println("Widget " + w + " name changed from name "
			+ nameBefore);
	}

	public void onWidgetAdded(WidgetInstance parent, WidgetInstance child)
	{
		System.out.println("Widget " + child + "added to " + parent);
	}

	public void onWidgetRemoved(WidgetInstance parent, WidgetInstance child,
		int index)
	{
	}

	public void onWidgetPropertyChanged(WidgetInstance widget,
		WidgetPropertyInstance prop)
	{
		System.out.println("Property changed for " + widget.getName() + " to: "
			+ prop.getValue());
	}

	public void onScreenShown(WidgetInstance screen)
	{
		System.out.println("Showing screen " + screen.getName());
	}

	public void onLuaEvaluate(String str)
	{
		System.out.println("Evaluating " + str);
	}
}

public class WidgetEditor extends Container implements ClipboardOwner
{
	private static final long serialVersionUID = 1L;

	public WidgetEditor(JFrame parent)
	{
		this.mParent = parent;
		initEditor();

		mWidgetEditorListeners = new ArrayList<WidgetEditorListener>();

		addWidgetEditorListener(new TestWidgetEditorListener());
	}

	public void addWidgetEditorListener(WidgetEditorListener listener)
	{
		mWidgetEditorListeners.add(listener);
	}

	public void fireOnWidgetCreated(WidgetInstance widget)
	{
		for (WidgetEditorListener l : mWidgetEditorListeners)
		{
			l.onWidgetCreated(widget);
		}
	}

	public void fireOnWidgetNameChanged(WidgetInstance w, String nameBefore,
		String nameAfter)
	{
		for (WidgetEditorListener l : mWidgetEditorListeners)
		{
			l.onWidgetNameChanged(w, nameBefore, nameAfter);
		}
	}

	public void fireOnWidgetAdded(WidgetInstance parent, WidgetInstance child)
	{
		for (WidgetEditorListener l : mWidgetEditorListeners)
		{
			l.onWidgetAdded(parent, child);
		}
	}

	public void fireOnWidgetRemoved(WidgetInstance parent,
		WidgetInstance child, int index)
	{
		for (WidgetEditorListener l : mWidgetEditorListeners)
		{
			l.onWidgetRemoved(parent, child, index);
		}
	}

	public void fireOnWidgetPropertyChanged(WidgetInstance widget,
		WidgetPropertyInstance prop)
	{
		for (WidgetEditorListener l : mWidgetEditorListeners)
		{
			l.onWidgetPropertyChanged(widget, prop);
		}
	}

	public void fireOnScreenShown(WidgetInstance screen)
	{
		for (WidgetEditorListener l : mWidgetEditorListeners)
		{
			l.onScreenShown(screen);
		}
	}

	public void fireOnLuaEvaluate(String str)
	{
		for (WidgetEditorListener l : mWidgetEditorListeners)
		{
			l.onLuaEvaluate(str);
		}
	}

	private Color decodePropertyColor(WidgetPropertyInstance pi)
	{
		String value = pi.getValue().trim();
		if (value.startsWith("0x") == true)
		{
			value = value.substring(2);
		}

		int alpha = 255;
		boolean hasAlpha = false;
		if (value.length() > 6)
		{
			String alphaPart = value.substring(0, value.length() - 6);
			value = value.substring(value.length() - 6);
			alpha = Integer.parseInt(alphaPart, 16);
			hasAlpha = true;
		}

		int rgb = Integer.parseInt(value, 16);
		return new Color(rgb | (alpha << 24), hasAlpha);
	}

	private String encodePropertyComponent(int c)
	{
		String cs = Integer.toHexString(c);
		if (cs.length() < 2)
			cs = "0" + cs;
		return cs;
	}

	private String encodePropertyColor(Color color)
	{
		int r = color.getRed();
		int g = color.getGreen();
		int b = color.getBlue();
		int a = color.getAlpha();
		return encodePropertyComponent(a) + encodePropertyComponent(r)
			+ encodePropertyComponent(g) + encodePropertyComponent(b);
	}

	private Component createComponentForWidgetPropertyInstance(
		final WidgetPropertyInstance propertyInstance)
	{
		Container layout = new Container();
		layout.setLayout(new GridLayout(1, 2));
		JLabel nameLabel = new JLabel(propertyInstance.getType().mName);
		nameLabel.setBackground(Color.RED);
		layout.add(nameLabel);

		Component controller;

		final String type = propertyInstance.getType().mType;

		if (type.equals("bool"))
		{
			JCheckBox checkBox = new JCheckBox("", propertyInstance.getValue()
				.equals("true") ? true : false);
			checkBox.addChangeListener(new ChangeListener()
			{
				public void stateChanged(ChangeEvent e)
				{
					AbstractButton abstractButton = (AbstractButton) e
						.getSource();
					boolean selected = abstractButton.getModel().isSelected();

					String value = "" + selected;
					propertyInstance.setValue(value);
					fireOnWidgetPropertyChanged(propertyInstance
						.getWidgetInstance(), propertyInstance);
				}
			});
			controller = checkBox;
		}
		else if (type.equals("int"))
		{
			final boolean isFloat = type.equals("float");
			JSpinner slider;
			if (isFloat == false)
				slider = new JSpinner();
			else
				slider = new JSpinner(new SpinnerNumberModel(0.0, -10000.0,
					10000.0, 0.00001));

			slider.addChangeListener(new ChangeListener()
			{
				public void stateChanged(ChangeEvent e)
				{
					JSpinner slider = (JSpinner) e.getSource();
					String value = "" + slider.getValue();
					propertyInstance.setValue(value);
					System.out.println("Setting value: " + value);
					fireOnWidgetPropertyChanged(propertyInstance
						.getWidgetInstance(), propertyInstance);
				}
			});
			if (isFloat)
			{
				SpinnerNumberModel model = (SpinnerNumberModel) slider
					.getModel();
				model.setStepSize(new Double(0.000001));
				slider.setValue(new Double(propertyInstance.getValue()));
			}
			else
				slider.setValue(new Integer(propertyInstance.getValue()));
			controller = slider;
		}
		else if (type.equals("string") || type.equals("float")
			|| type.equals("resource") || type.equals("enum"))
		{
			final JTextField field = new JTextField();
			field.addActionListener(new ActionListener()
			{
				public void actionPerformed(ActionEvent e)
				{
					System.out.println("Setting text: " + field.getText());
					propertyInstance.setValue(field.getText());
					fireOnWidgetPropertyChanged(propertyInstance
						.getWidgetInstance(), propertyInstance);
				}
			});
			field.setText(propertyInstance.getValue());
			controller = field;
		}
		else if (type.equals("color"))
		{
			Container container = new Container();
			container.setLayout(new GridLayout(1, 2));
			final JPanel color = new JPanel();
			color.setBackground(decodePropertyColor(propertyInstance));

			final JTextField field = new JTextField();
			field.setColumns(8);
			field.setText(propertyInstance.getValue());

			container.add(color);
			container.add(field);

			field.addActionListener(new ActionListener()
			{
				public void actionPerformed(ActionEvent e)
				{
					System.out.println("Setting text: " + field.getText());
					propertyInstance.setValue(field.getText());
					color.setBackground(decodePropertyColor(propertyInstance));
					fireOnWidgetPropertyChanged(propertyInstance
						.getWidgetInstance(), propertyInstance);
				}
			});

			color.addMouseListener(new MouseListener()
			{
				public void mouseReleased(MouseEvent e)
				{
				}

				public void mousePressed(MouseEvent e)
				{
				}

				public void mouseExited(MouseEvent e)
				{
				}

				public void mouseEntered(MouseEvent e)
				{
				}

				public void mouseClicked(MouseEvent e)
				{
					Color newColor = JColorChooser.showDialog(mParent,
						"Choose color", decodePropertyColor(propertyInstance));
					String hexString = encodePropertyColor(newColor);
					propertyInstance.setValue(encodePropertyColor(newColor));
					field.setText(hexString);
					color.setBackground(newColor);
				}
			});

			controller = container;
		}
		else
			return null;

		layout.add(controller);

		return layout;
	}

	private void populateLayoutForWidgetInstance(WidgetInstance widgetInstance)
	{
		System.out.println("createEditingComponent() for "
			+ widgetInstance.getName());
		Container layout = mPropertyLayout;
		layout.removeAll();
		// layout.setLayout(new
		// GridLayout(widgetInstance.getProperties().size(),1));
		layout.setLayout(new BoxLayout(layout, BoxLayout.PAGE_AXIS));

		mNameEdit.setText(widgetInstance.getName());

		Map<String, WidgetPropertyInstance> properties = widgetInstance
			.getProperties();

		for (WidgetPropertyInstance t : properties.values())
		{
			System.out.println("Adding ptype: " + t.getType().mName);
			layout.add(createComponentForWidgetPropertyInstance(t));
		}

		layout.validate();
	}

	void addWidgetToHierarchyTree(DefaultMutableTreeNode parent,
		WidgetInstance i)
	{
		System.out.println("Woho, adding node...");
		DefaultMutableTreeNode uiNode = new DefaultMutableTreeNode(i.getName());
		uiNode.setUserObject(i);
		parent.add(uiNode);

		for (WidgetInstance c : i.getChildren())
		{
			addWidgetToHierarchyTree(uiNode, c);
		}
	}

	void updateWidgetHierarchyTree()
	{
		mTreeTop.removeAllChildren();

		for (WidgetInstance i : mScreens)
		{
			addWidgetToHierarchyTree(mTreeTop, i);
		}
	}

	WidgetInstance getSelectedWidgetInstance()
	{
		DefaultMutableTreeNode node = (DefaultMutableTreeNode) mTree
			.getLastSelectedPathComponent();
		if (node == null)
			return null;
		Object nodeInfo = node.getUserObject();
		if (nodeInfo instanceof WidgetInstance && nodeInfo != null)
		{
			WidgetInstance i = (WidgetInstance) nodeInfo;
			return i;
		}
		return null;
	}

	void updateSelectedWidgetInstance()
	{
		DefaultMutableTreeNode node = (DefaultMutableTreeNode) mTree
			.getLastSelectedPathComponent();
		if (node == null)
			return;
		DefaultTreeModel model = ((DefaultTreeModel) mTree.getModel());
		model.nodeChanged(node);
	}

	// If expand is true, expands all nodes in the tree.
	// Otherwise, collapses all nodes in the tree.
	public void expandAll(JTree tree, boolean expand)
	{
		TreeNode root = (TreeNode) tree.getModel().getRoot();

		// Traverse tree from root
		expandAll(tree, new TreePath(root), expand);
	}

	@SuppressWarnings("unchecked")
	private void expandAll(JTree tree, TreePath parent, boolean expand)
	{
		// Traverse children
		TreeNode node = (TreeNode) parent.getLastPathComponent();
		if (node.getChildCount() >= 0)
		{
			for (Enumeration e = node.children(); e.hasMoreElements();)
			{
				TreeNode n = (TreeNode) e.nextElement();
				TreePath path = parent.pathByAddingChild(n);
				expandAll(tree, path, expand);
			}
		}

		// Expansion or collapse must be done bottom-up
		if (expand)
		{
			tree.expandPath(parent);
		}
		else
		{
			tree.collapsePath(parent);
		}
	}

	void showScreenForWidget(WidgetInstance w)
	{
		WidgetInstance parent = w.getParent();
		if (parent == null)
		{
			if (!w.getType().getName().endsWith("Screen"))
			{
				System.out.println("Root node that is not a screen. Bad.");
			}
			else
			{
				fireOnScreenShown(w);
			}
		}
		else
		{
			showScreenForWidget(parent);
		}
	}

	public JComponent createTopContent()
	{
		JPanel main = new JPanel(new BorderLayout());
		main.setBackground((Color.RED));

		/* -------- Toolbar ---------- */
		JPanel toolbar = new JPanel();
		// toolbar.setBackground(Color.YELLOW);
		toolbar.setMinimumSize(new Dimension(100, 300));
		toolbar.setBounds(0, 0, 800, 100);

		final JComboBox widgetCombo = new JComboBox(WidgetType.getTypes());

		toolbar.add(widgetCombo);
		final JButton createWidgetButton = new JButton("Add widget");
		toolbar.add(createWidgetButton);
		final JButton loadXMLButton = new JButton("Load XML...");
		loadXMLButton.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				loadXML();
				expandAll(mTree, true);
			}
		});
		toolbar.add(loadXMLButton);
		final JButton saveXMLButton = new JButton("Save XML...");
		saveXMLButton.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				saveXML();
			}
		});
		toolbar.add(saveXMLButton);
		final JButton cppButton = new JButton("Cpp");
		cppButton.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				cppToClipboard();
			}
		});
		toolbar.add(cppButton);
		final JButton luaButton = new JButton("Lua");
		luaButton.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				luaToClipboard();
			}
		});
		toolbar.add(luaButton);
		main.add(toolbar, BorderLayout.PAGE_START);

		final WidgetEditor editor = this;

		createWidgetButton.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent evt)
			{
				System.out.println("Selected item:"
					+ widgetCombo.getSelectedItem());
				DefaultMutableTreeNode node = (DefaultMutableTreeNode) mTree
					.getLastSelectedPathComponent();
				if (node == null)
					return;
				System.out.println("Selected node:"
					+ mTree.getLastSelectedPathComponent().toString());

				WidgetType wType = (WidgetType) widgetCombo.getSelectedItem();

				// UNUSED: int selIndex = widgetCombo.getSelectedIndex();
				WidgetInstance i = new WidgetInstance(wType, wType.getName()
					+ mIdCounter);
				mIdCounter++;
				fireOnWidgetCreated(i);

				WidgetInstance parent = null;
				if (node.getUserObject() instanceof WidgetInstance)
				{
					parent = (WidgetInstance) node.getUserObject();
					parent.addChild(i);
					fireOnWidgetAdded(parent, i);
				}
				else
				{
					// validate
					mScreens.add(i);
				}

				DefaultMutableTreeNode uiNode = new DefaultMutableTreeNode(i
					.getName());
				uiNode.setUserObject(i);
				((DefaultTreeModel) mTree.getModel()).insertNodeInto(uiNode,
					node, node.getChildCount());
				System.out.println("Should have added node " + wType.getName());
				expandAll(mTree, true);

				showScreenForWidget(i);

				editor.validate();
			}
		});

		/* -------- Widget Tree ---------- */
		// create tree view
		mTreeTop = new DefaultMutableTreeNode("UI");
		updateWidgetHierarchyTree();
		mTree = new JTree(mTreeTop);
		mTree.getSelectionModel().setSelectionMode(
			TreeSelectionModel.SINGLE_TREE_SELECTION);
		mTree.addTreeSelectionListener(new TreeSelectionListener()
		{
			public void valueChanged(TreeSelectionEvent e)
			{
				DefaultMutableTreeNode node = (DefaultMutableTreeNode) mTree
					.getLastSelectedPathComponent();
				if (node == null)
					return;
				Object nodeInfo = node.getUserObject();
				if (nodeInfo instanceof WidgetInstance && nodeInfo != null)
				{
					WidgetInstance i = (WidgetInstance) nodeInfo;
					editor.setWidgetInstance(i);
					showScreenForWidget(i);
				}

				editor.validate();
			}
		});

		JScrollPane treeScrollView = new JScrollPane(mTree);
		treeScrollView.setBackground(Color.GREEN);
		main.add(treeScrollView, BorderLayout.CENTER);
		main.setMinimumSize(new Dimension(100, 50));

		/* -------- Bottom toolbar ---------- */
		JPanel toolbarBottom = new JPanel();
		// toolbarBottom.setBackground(Color.YELLOW);
		toolbarBottom.setMinimumSize(new Dimension(100, 300));
		toolbarBottom.setBounds(0, 0, 800, 100);

		final JLabel nameLabel = new JLabel("Widget name:");
		mNameEdit = new JTextField();
		mNameEdit.setColumns(10);

		toolbarBottom.add(nameLabel);
		toolbarBottom.add(mNameEdit);
		final JButton renameButton = new JButton("Rename");
		toolbarBottom.add(renameButton);
		final JButton removeButton = new JButton("Remove");
		toolbarBottom.add(removeButton);
		main.add(toolbarBottom, BorderLayout.PAGE_END);

		renameButton.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent evt)
			{
				WidgetInstance selectedInstance = getSelectedWidgetInstance();
				if (selectedInstance == null)
					return;
				String oldName = selectedInstance.getName();
				String newName = mNameEdit.getText();
				if (newName.equals(""))
					return;
				selectedInstance.setName(newName);
				fireOnWidgetNameChanged(selectedInstance, oldName, newName);
				updateSelectedWidgetInstance();
			}
		});

		removeButton.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent evt)
			{
				WidgetInstance selectedInstance = getSelectedWidgetInstance();
				if (selectedInstance == null)
					return;

			}
		});

		return main;
	}

	public JComponent createBottomContent()
	{
		// JPanel bottomContent = new JPanel(new BorderLayout());
		Container bottomContent = mPropertyLayout = new Container();
		// bottomContent.setBackground(Color.RED);
		Dimension minimumSize = new Dimension(100, 50);
		bottomContent.setMinimumSize(minimumSize);

		return new JScrollPane(bottomContent);
	}

	public JComponent createWidgetEditorUI()
	{
		JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
			createTopContent(), createBottomContent());
		splitPane.setOneTouchExpandable(true);
		splitPane.setDividerLocation(300);
		return splitPane;
	}

	void initEditor()
	{
		// init temp data
		mIdCounter = 0;

		WidgetType.initTypes();

		mScreens = new ArrayList<WidgetInstance>();

		// end create tree view
		this.setLayout(new BorderLayout());
		this.add(createWidgetEditorUI(), BorderLayout.CENTER);
	}

	void setWidgetInstance(WidgetInstance widgetInstance)
	{
		populateLayoutForWidgetInstance(widgetInstance);
	}

	void loadXML()
	{
		JFileChooser fc = new JFileChooser(new File("."));
		fc.setCurrentDirectory(new File("."));
		fc.showOpenDialog(this);
		File selectedFile = fc.getSelectedFile();

		if (null == selectedFile)
		{
			return;
		}

		SAXParser sp = new SAXParser();
		WidgetParser myHandler = new WidgetParser();
		sp.setContentHandler(myHandler);
		try
		{
			sp.parse(selectedFile.getAbsolutePath());
		}
		catch (Exception e)
		{
			e.printStackTrace();
			System.exit(0);
		}

		mScreens = myHandler.getScreens();
		System.out.println(mScreens.get(0).toXML(0));

		String code = "";
		for (WidgetInstance i : mScreens)
		{
			code += i.toLua();
		}
		fireOnLuaEvaluate(code);

		mTreeTop.removeAllChildren();
		((DefaultTreeModel) mTree.getModel()).reload();
		updateWidgetHierarchyTree();
	}

	void saveXML()
	{
		System.out.println(mScreens.get(0).toXML(0));
		JFileChooser fc = new JFileChooser(".");
		fc.setSelectedFile(new File(""));
		fc.showSaveDialog(this);
		File selectedFile = fc.getSelectedFile();
		if (null == selectedFile)
		{
			return;
		}

		try
		{
			// Write file.
			Writer output = new BufferedWriter(new FileWriter(selectedFile));
			output.write("<UI>\n");
			for (WidgetInstance i : mScreens)
			{
				output.write(i.toXML(1));
			}
			output.write("</UI>\n");
			output.close();

			// Sets window title to show the filename.
			// mSelf.setCurrentFileName(selectedFile.getName());
		}
		catch (IOException ex)
		{
			ex.printStackTrace();
		}

	}

	private static ArrayList<String> allHandles = new ArrayList<String>();

	void cppToClipboard()
	{
		String ret = "";
		ret += "class UI {\n";
		ret += "public:\n";
		allHandles.clear();
		for (WidgetInstance i : mScreens)
		{
			if (!i.getType().getName().equals("TabScreen"))
			{
				ret += "\tvoid setup" + i.getName() + "() {\n";
				ret += i.toCppStep(allHandles, null);
				ret += "\t}\n";
			}
			else
			{
				ArrayList<WidgetInstance> children = i.getChildren();
				ret += "\tvoid setup" + i.getName() + "() {\n";
				ret += "\t\t" + i.getName()
					+ " = maWidgetCreate(\"TabScreen\");\n";
				allHandles.add(i.getName());
				for (WidgetInstance j : children)
				{
					ret += "\t\tsetup" + j.getName() + "();\n";
				}
				ret += "\t}\n";
				for (WidgetInstance j : children)
				{
					ret += "\tMAHandle setup" + j.getName() + "() {\n";
					ret += j.toCppStep(allHandles, i);
					ret += "\t\treturn " + j.getName() + ";\n";
					ret += "\t}\n";
				}
			}
		}
		for (String s : allHandles)
		{
			ret += "\tMAHandle " + s + ";\n";
		}
		ret += "};\n";
		// return ret;

		System.out.println(ret);
		StringSelection contents = new StringSelection(ret);
		Clipboard clipboard = getToolkit().getSystemClipboard();
		clipboard.setContents(contents, this);
	}

	void luaToClipboard()
	{
		String code = "";
		for (WidgetInstance i : mScreens)
		{
			code += i.toLua();
		}
		System.out.println(code);
		StringSelection contents = new StringSelection(code);
		Clipboard clipboard = getToolkit().getSystemClipboard();
		clipboard.setContents(contents, this);
	}

	public void lostOwnership(Clipboard clipboard, Transferable contents)
	{
		System.out.println("Clipboard contents replaced");
	}

	private JFrame mParent;
	private int mIdCounter;
	private JTextField mNameEdit;
	private ArrayList<WidgetInstance> mScreens;
	private Container mPropertyLayout;
	private JTree mTree;
	private DefaultMutableTreeNode mTreeTop;

	private ArrayList<WidgetEditorListener> mWidgetEditorListeners;
}
